package za.co.trojanc.ini_lib;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

/**
 * A class used to read an ini file.
 * @author Charl Thiem
 * @since 09 Sep 2009
 */
public class INIReader {

	/** File that needs to be read */
	private File iniFile;

	/** Flag if the comments and whitespace should be kept */
	private boolean readComments = false;
	
	
	/**
	 * Creates a new instance of <code>INIReader</code>.
	 * @param file The file that needs to be read
	 */
	public INIReader(File file) {
		this.iniFile = file;
	}
	
	/**
	 * Creates a new instance of <code>INIReader</code>.
	 * @param filePath String representing the path to the ini file.
	 */
	public INIReader (String filePath) {
		this(new File(filePath));
	}
	
	/**
	 * Reads the configuration file and returns the configuration in an 
	 * <code>INIConfig</code> object.
	 * @return
	 */
	public INIConfig readConfig() throws IOException{
		/* Reader that will read the file */
		BufferedReader reader = new BufferedReader(new FileReader(this.iniFile));
		/* Section to which is currently read */
		INISection activeSection = null;
		/* Line number of line currently read */
		int lineNumber = 0;
		/* Text that preceeds the item that follows */
		StringBuilder preSpace = null;
		
		INIConfig config;
		try {
			config = new INIConfig();
			String line = reader.readLine();
			while (line != null) {
				lineNumber++;
				line.trim();
				
				/*
				 * First check if the line is not comment or blank
				 */
				if (line.startsWith("#") || line.startsWith(";") || line.length() == 0) {;
					if (this.readComments) {
						if (preSpace == null) {
							preSpace = new StringBuilder();
							preSpace.append(line);
						}
						else {
							preSpace.append("\n").append(line);
						}
					}
				}
				/*
				 * Okay so it is not comment or a blank line, maybe it is something useful
				 */
				else {
					/*
					 * Is this line an configuration entry?
					 */
					boolean flag = false; // Flag if something useful was found.
					int index1 = -1;
					int index2 = -1;
					if ((index1 = line.indexOf("=")) > 0) {
						flag = true;
						String name = line.substring(0, index1);
						String value = line.substring(index1+1);
						INIField field = new INIField(name, value);
						if (this.readComments && preSpace != null) {
							/*
							 * If there are preceding spaces, add it
							 */
							field.setPreText(preSpace.toString());
							preSpace = null;
						}
//					System.out.println("Adding field : " + field.getName()+","+field.getValue());
						activeSection.addField(field);
					}
					
					/*
					 * Is it the start of an section
					 */
					index1 = line.indexOf("[");
					index2 = line.indexOf("]");
					if ( (index1 == 0) && (index2 > (index1+1)) ) {
						flag = true;
						String sectionName = line.substring(index1+1, index2);
						INISection section = new INISection(sectionName);
						if (this.readComments && preSpace != null) {
							/*
							 * If there are preceding spaces, add it
							 */
							section.setPreText(preSpace.toString());
							preSpace = null;
						}
//					System.out.println("Adding section : " + section.getName());
						config.addSection(section);
						activeSection = section;
					}
					
					if (!flag) {
						/*
						 * Nothing useful was found so there must be an error at this line
						 */
						throw new IllegalArgumentException(new StringBuilder("Invalid line : ").append(lineNumber).append("\"").append(line).append("\"").toString());
					}
				}
				
				line = reader.readLine();
			}
		} finally {
			reader.close();
		}
		return config;
	}
	
	/**
	 * Sets if comments should be read into configuration.
	 * @param flag True if comments should be read.
	 */
	public void setReadComments(boolean flag) {
		this.readComments = flag;
	}
	
	/**
	 * Unit test
	 * 
	 * @param args
	 */
	public static void main(String...args) {
		try {
			INIReader reader = new INIReader("D:\\my_work\\My Documents\\test.ini");
			reader.setReadComments(true);
			INIConfig config = reader.readConfig();
			INIWriter writer = new INIWriter("D:\\my_work\\My Documents\\test000.ini");
			writer.setWriteComments(true);
			writer.writeConfig(config);
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}
}
